#include "stdafx.h"
#include "AsioSession.h"

bool Session::Connect()
{
	if (!DoCreateSocketAndConnect(mConnStr))
		return false;
	AsyncReadSome();
	return true;
}

Session::~Session()
{
	if (IsConnected())
		Close();
}

void Session::handle_read(const boost::system::error_code& error, size_t bytes_transferred)
{
	std::string s(mRecvBuf, bytes_transferred);
	{
		boost::unique_lock<boost::mutex> _(mLock);
		mMessageQueue.push_back(s);
		Record(s, true, true);
	}
	AsyncReadSome();
}

std::string Session::GetMessage()
{
	boost::unique_lock<boost::mutex> _(mLock);
	if (mMessageQueue.empty())
		return "";
	std::string message = mMessageQueue.front();
	mMessageQueue.pop_front();
	return message;
}

bool Session::SendSome(const std::string& message)
{
	bool ret = DoSendSome(message);
	Record(message, false, ret);
	return ret;
}

void Session::Record(const std::string& message, bool isrecv, bool success)
{
	if (mEnableRecord && !mRecordDir.empty())
	{

	}
}

void Session::SetConnectStr( const std::string& connstr )
{
	mConnStr = connstr;
}

std::string Session::GetCombinedMessage()
{
	boost::unique_lock<boost::mutex> _(mLock);
	std::string message;

	for (MessageQueue::iterator it = mMessageQueue.begin(); it != mMessageQueue.end(); ++it)
	{
		message += *it;
	}
	mMessageQueue.clear();
	return message;
}

std::string Session::GetCombinedMessageByLine( int count )
{
	boost::unique_lock<boost::mutex> _(mLock);
	std::string message;

	for (MessageQueue::iterator it = mMessageQueue.begin(); it != mMessageQueue.end() && count>0;--count)
	{
		message += *it;
		it = mMessageQueue.erase(it);
	}

	return message;
}

std::string Session::GetCombinedMessageByCount( int count )
{
	boost::unique_lock<boost::mutex> _(mLock);
	std::string message;

	for (MessageQueue::iterator it = mMessageQueue.begin(); it != mMessageQueue.end() && count>0;)
	{
		int len = it->size();
		if (count-len > 0)
		{
			message += *it;
			it = mMessageQueue.erase(it);
			count -= len;
		}
		else if (count-len < 0)
		{
			std::string msg = *it;
			message += msg.substr(0, count);
			*it = msg.substr(count);
			count = 0;
			break;
		}
	}

	return message;
}

std::string Session::GetCombinedMessageByEndchars( const std::string& endchars )
{
	boost::unique_lock<boost::mutex> _(mLock);
	std::string message;

	MessageQueue::iterator it;
	std::string::size_type pos = std::string::npos;
	int lineno = 0;

	for (it = mMessageQueue.begin(); it != mMessageQueue.end(); ++it, ++lineno)
	{
		std::string msg = *it;
		pos = msg.find(endchars);
		if (pos != std::string::npos)
		{
			break;
		}
	}

	if (it != mMessageQueue.end() && pos != std::string::npos)
	{
		for (MessageQueue::iterator it2 = mMessageQueue.begin(); it2 != mMessageQueue.end() && lineno >= 0; ++it2, --lineno)
		{
			if (lineno == 0)
			{
				std::string msg = *it2;
				message += msg.substr(0, pos);
				*it2 = msg.substr(pos+endchars.length());
				if (it2->empty())
					mMessageQueue.erase(it2);
				return message;
			}
			else
			{
				message += *it2;
				it2 = mMessageQueue.erase(it2);
			}
		}
	}
	return message;
}

boost::atomics::atomic_bool Session::mEnableRecord;

std::string Session::mRecordDir;

bool UDPSession::DoSendSome(const std::string& message)
{
	if (mSocket == NULL)
		return false;
	return mSocket->send_to(boost::asio::buffer(message.data(), message.length()), sender_endpoint_) == message.length();
}

bool UDPSession::DoCreateSocketAndConnect(const std::string& connstr)
{
	std::string address;
	int port = -1;

	size_t pos = connstr.find(':');
	if (pos == std::string::npos)
		port = atoi(connstr.c_str());
	else
	{
		address = connstr.substr(0, pos);
		port = atoi(connstr.substr(pos + 1).c_str());
	}

	if (port <= 0)
		return false;

	if (address.empty())
		mSocket = new udp::socket(mIOService, udp::endpoint(udp::v4(), port));
	else
		mSocket = new udp::socket(mIOService, udp::endpoint(address::from_string(address), port));

	return true;
}

void UDPSession::AsyncReadSome()
{
	mSocket->async_receive_from(boost::asio::buffer(mRecvBuf, max_length), sender_endpoint_, boost::bind(&SocketSession < udp::socket>::handle_read, shared_from_this(),
		boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}

bool TCPSession::DoCreateSocketAndConnect(const std::string& connstr)
{
	std::string address;
	int port = -1;

	size_t pos = connstr.find(':');
	if (pos == std::string::npos)
		return false;

	address = connstr.substr(0, pos);
	port = atoi(connstr.substr(pos + 1).c_str());

	if (port <= 0)
		return false;

	mSocket = new tcp::socket(mIOService);
	server_endpoint = tcp::endpoint(address::from_string(address), port);

	boost::system::error_code ec;
	mSocket->connect(server_endpoint, ec);
	return ec;
}

bool SerialSession::DoCreateSocketAndConnect(const std::string& connstr)
{
	std::string port;
	
	size_t pos1 = connstr.find(',');
	if (pos1== std::string::npos)
		return false;
	port = connstr.substr(0, pos1);

	mSocket = new boost::asio::serial_port(mIOService, "COM3");

	size_t pos2 = connstr.find(',', pos1 + 1);
	int baud = 9600;
	if (pos1!= std::string::npos)
		baud = atoi(connstr.substr(pos1 + 1, pos2).c_str());
	mSocket->set_option(boost::asio::serial_port::baud_rate(baud));
	pos1 = pos2;
	pos2 = connstr.find(',', pos1 + 1);
	int databits = 8;
	if (pos1 != std::string::npos)
		databits = atoi(connstr.substr(pos1 + 1, pos2).c_str());
	mSocket->set_option(boost::asio::serial_port::character_size(databits));
	pos1 = pos2;
	pos2 = connstr.find(',', pos1 + 1);
	std::string parity = "N";
	if (pos1 != std::string::npos)
		parity = connstr.substr(pos1 + 1, pos2);
	if (parity == "o")
		mSocket->set_option(boost::asio::serial_port::parity(boost::asio::serial_port::parity::odd));
	else if (parity == "E")
		mSocket->set_option(boost::asio::serial_port::parity(boost::asio::serial_port::parity::even));
	else
		mSocket->set_option(boost::asio::serial_port::parity(boost::asio::serial_port::parity::none));
	pos1 = pos2;
	pos2 = connstr.find(',', pos1 + 1);
	std::string stopbits = "1";
	if (pos1 != std::string::npos)
		stopbits = connstr.substr(pos1 + 1, pos2);

	if (stopbits == "1")
		mSocket->set_option(boost::asio::serial_port::stop_bits(boost::asio::serial_port::stop_bits::one));
	else if (stopbits == "2")
		mSocket->set_option(boost::asio::serial_port::stop_bits(boost::asio::serial_port::stop_bits::two));
	else
		mSocket->set_option(boost::asio::serial_port::stop_bits(boost::asio::serial_port::stop_bits::onepointfive));

	mSocket->set_option(boost::asio::serial_port::flow_control());

	return true;
}
